import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score, accuracy_score
from sklearn.model_selection import GridSearchCV
# 加载数据
data = pd.read_excel('C:/Users/Administrator/Desktop/股票客户流失.xlsx')
# 查看数据基本信息
print(data.info())
print(data.describe())
print(data['是否流失'].value_counts(normalize=True))
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
# 设置绘图风格
plt.style.use('ggplot')
# 流失比例饼图
plt.figure(figsize=(8,6))
data['是否流失'].value_counts().plot.pie(autopct='%1.1f%%', colors=['#66b3ff','#ff9999'])
plt.title('客户流失比例')
plt.ylabel('')
plt.show()
# 数值特征分布
num_cols = ['账户资金（元）', '最后一次交易距今时间（天）', '上月交易佣金（元）', '累计交易佣金（元）', '本券商使用时长（年）']
plt.figure(figsize=(15,10))
for i, col in enumerate(num_cols, 1):
    plt.subplot(2,3,i)
    sns.histplot(data[col], kde=True, bins=30)
    plt.title(f'{col}分布')
plt.tight_layout()
plt.show()

# 特征与流失的相关性
plt.figure(figsize=(10,8))
sns.heatmap(data.corr(), annot=True, cmap='coolwarm', center=0)
plt.title('特征相关性热图')
plt.show()
# 检查缺失值
print(data.isnull().sum())

# 处理异常值 - 使用IQR方法
def handle_outliers(df, col):
    Q1 = df[col].quantile(0.25)
    Q3 = df[col].quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    df[col] = np.where(df[col] < lower_bound, lower_bound, df[col])
    df[col] = np.where(df[col] > upper_bound, upper_bound, df[col])
    return df

for col in num_cols:
    data = handle_outliers(data, col)

# 创建新特征
data['资金使用率'] = data['上月交易佣金（元）'] / data['账户资金（元）']
data['资金使用率'] = data['资金使用率'].replace([np.inf, -np.inf], np.nan).fillna(0)
data['日均佣金'] = data['累计交易佣金（元）'] / (data['本券商使用时长（年）'] * 365 + 1)  # 防止除以0
# 特征与目标变量
X = data.drop('是否流失', axis=1)
y = data['是否流失']
# 标准化数值特征
scaler = StandardScaler()
X[num_cols] = scaler.fit_transform(X[num_cols])
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
print(f"训练集形状: {X_train.shape}, 测试集形状: {X_test.shape}")
print(f"训练集流失比例: {y_train.mean():.2%}, 测试集流失比例: {y_test.mean():.2%}")
# 逻辑回归模型
lr = LogisticRegression(random_state=42, max_iter=1000)
lr.fit(X_train, y_train)

# 预测与评估
y_pred_lr = lr.predict(X_test)
y_proba_lr = lr.predict_proba(X_test)[:,1]

print("逻辑回归模型性能:")
print(classification_report(y_test, y_pred_lr))
print(f"准确率: {accuracy_score(y_test, y_pred_lr):.4f}")
print(f"AUC分数: {roc_auc_score(y_test, y_proba_lr):.4f}")

# 混淆矩阵
plt.figure(figsize=(6,6))
sns.heatmap(confusion_matrix(y_test, y_pred_lr), annot=True, fmt='d', cmap='Blues')
plt.title('逻辑回归混淆矩阵')
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.show()
# 随机森林模型
rf = RandomForestClassifier(random_state=42)
rf.fit(X_train, y_train)

# 预测与评估
y_pred_rf = rf.predict(X_test)
y_proba_rf = rf.predict_proba(X_test)[:,1]

print("\n随机森林模型性能:")
print(classification_report(y_test, y_pred_rf))
print(f"准确率: {accuracy_score(y_test, y_pred_rf):.4f}")
print(f"AUC分数: {roc_auc_score(y_test, y_proba_rf):.4f}")

# 混淆矩阵
plt.figure(figsize=(6,6))
sns.heatmap(confusion_matrix(y_test, y_pred_rf), annot=True, fmt='d', cmap='Greens')
plt.title('随机森林混淆矩阵')
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.show()
# 特征重要性
feature_importance = pd.DataFrame({
    '特征': X.columns,
    '重要性': rf.feature_importances_
}).sort_values('重要性', ascending=False)

plt.figure(figsize=(10,6))
sns.barplot(x='重要性', y='特征', data=feature_importance)
plt.title('随机森林特征重要性')
plt.show()
# 随机森林参数网格
param_grid = {
    'n_estimators': [100, 200, 300],
    'max_depth': [None, 10, 20, 30],
    'min_samples_split': [2, 5, 10],
    'min_samples_leaf': [1, 2, 4]
}
# 网格搜索
grid_search = GridSearchCV(estimator=RandomForestClassifier(random_state=42),
                          param_grid=param_grid,
                          cv=5,
                          n_jobs=-1,
                          scoring='roc_auc')

grid_search.fit(X_train, y_train)
# 最佳参数
print("最佳参数:", grid_search.best_params_)
# 使用最佳参数重新训练
best_rf = grid_search.best_estimator_
y_pred_best = best_rf.predict(X_test)
y_proba_best = best_rf.predict_proba(X_test)[:,1]
print("\n优化后的随机森林模型性能:")
print(classification_report(y_test, y_pred_best))
print(f"准确率: {accuracy_score(y_test, y_pred_best):.4f}")
print(f"AUC分数: {roc_auc_score(y_test, y_proba_best):.4f}")
# 比较不同模型的AUC分数
models = ['逻辑回归', '随机森林', '优化随机森林']
auc_scores = [roc_auc_score(y_test, y_proba_lr),
              roc_auc_score(y_test, y_proba_rf),
              roc_auc_score(y_test, y_proba_best)]

plt.figure(figsize=(8,6))
sns.barplot(x=models, y=auc_scores, palette='viridis')
plt.title('模型AUC分数比较')
plt.ylabel('AUC分数')
plt.ylim(0.7, 1.0)
plt.show()